<?php /* УТФ-8:fix */

class SPIstore__pgsql
{
    private $storecfg   = null;
    private $db_handler = null;
    private $stat       = null;

    private $last_error_query;
    private $last_error_message;

    public function __construct( $storecfg )
    {
        $this->storecfg = $storecfg;

        $this->stat = array(
            'queries_count'     => 0,
            'queries_exectime'  => 0,
            'queries_repeat'    => 0,
            'queries_logs'      => array(),
        );

        return true;
    }

    public function connect()
    {
        if( is_resource( $this->db_handler ) === true )
        {
            return true;
        }

        $this->db_handler = pg_connect( " host="     . $this->storecfg['hostname']
                                      . " user= "    . $this->storecfg['username']
                                      . " password=" . $this->storecfg['password']
                                      . " dbname="   . $this->storecfg['database']
                                      );


        if( is_resource( $this->db_handler ) === false )
        {
            trigger_error( 'Cannot connect to database', E_USER_WARNING );
            return false;
        }

        if( isset( $this->storecfg['encoding'] ) )
        {
            $qresult = $this->query( "SET client_encoding TO '{$this->storecfg['encoding']}';", null, true );
        }

        if( isset( $this->storecfg['search_path'] ) )
        {
            $qresult = $this->query( "SET search_path TO {$this->storecfg['search_path']};", null, true );
        }

        return true;
    }

    public function disconnect()
    {
        if( is_resource( $this->db_handler ) === false )
        {
            return true;
        }

        pg_close( $this->db_handler );

        $this->db_handler = null;

        return true;
    }

    public function query( $query, $params = null, $is_autoclose = false )
    {
        if( $this->connect() === false )
        {
            return false;
        }

        // --------------------------------------------------------------------

        $fix_exectime_start = SPI::I('util')->get_microtime();

        $qresult = new SPIstore__pgsql__query( $this->db_handler, $query, $params );

        $this_exectime = SPI::I('util')->get_microtime() - $fix_exectime_start;

        $this->stat['queries_count']    += 1;
        $this->stat['queries_exectime'] += $this_exectime;

        $querykey = md5( $query );

        if( isset( $this->stat['queries_logs'][$querykey] ) === false )
        {
            $this->stat['queries_logs'][$querykey] = array(
                'query'     => $query,
                'count'     => 1,
                'exectime'  => $this_exectime,
            );
        }
        else
        {
            $this->stat['queries_logs'][$querykey]['count']++;
            $this->stat['queries_logs'][$querykey]['exectime'] += $this_exectime;
            $this->stat['queries_repeat'] += 1;
        }

        // --------------------------------------------------------------------

        if( $qresult->is_failed() )
        {
            $this->last_error_query   = $query;
            $this->last_error_message = $qresult->get_last_error_message();

            trigger_error( "Query failed [{$this->last_error_message}] [{$this->last_error_query}]", E_USER_WARNING );

            unset( $qresult );
            return false;
        }

        if( $is_autoclose === true )
        {
            $qresult->close();
            unset( $qresult );
            return true;
        }

        return $qresult;
    }

    public function insert( $tablename, $const_vars, $query_vars = null, $sequences_gen = null )
    {
        if( $this->connect() === false )
        {
            return false;
        }

        # ---------------------------------------------------------------------

        if( is_array( $const_vars ) === false )
        {
            $const_vars = array();
        }
        
        if( is_array( $query_vars ) === false )
        {
            $query_vars = array();
        }
        else
        {
            foreach( $query_vars as $key => $value )
            {
                unset( $const_vars[ $key ] );
            }
        }

        # ---------------------------------------------------------------------
        
        $seqs_array = array();
        
        if( is_array( $sequences_gen ) === true )
        foreach( $sequences_gen as $key => $seq_id )
        {
            $qresult = $this->query( " SELECT nextval('".$seq_id."'::regclass ) AS new_id " );

            if( $qresult === false )
            {
                return false;
            }
            
            $seq_item = $qresult->fetch();
            $qresult->close();
            unset( $qresult );
            
            $seqs_array[ $key ] = $seq_item['new_id'];
            $const_vars[ $key ] = $seq_item['new_id'];
        }
        
        # ---------------------------------------------------------------------
        
        if( count( $const_vars ) == 0 && count( $query_vars ) == 0 )
        {
            return false;
        }

        $result_a = '';
        $result_b = '';
        
        //---[ Generate const:vars ]---------------------------------

        foreach( $const_vars as $key => $value )
        {
            $result_a .= ( $result_a === '' ? '' : ', ' ) . '"' . $key . '"';
            $result_b .= ( $result_b === '' ? '' : ', ' );

            if( $value === null )
            {
                $result_b .= ' NULL ';
            }
            elseif( is_numeric( $value ) === true )
            {
                $result_b .= $value;
            }
            else
            {
                $result_b .= "'" . addslashes( (string) $value ) . "'";
            }
        }

        //---[ Generate query:vars ]---------------------------------

        foreach( $query_vars as $key => $value )
        {
            $result_a .= ( $result_a === '' ? '' : ', ' ) . '"' . $key . '"';
            $result_b .= ( $result_b === '' ? '' : ', ' ) . '(' . $value . ')';
        }

        //---[ Generate query ]--------------------------------------

        $query = ' INSERT INTO ' . $tablename . ' (' . $result_a . ') VALUES (' . $result_b . ') '
               ; 

        $qresult = $this->query( $query, null, true );
    
        if( $qresult === false )
        {
            return false;
        }
        
        if( count( $seqs_array ) > 0 )
        {
            return $seqs_array;
        }

        return true;
    }

    public function modify( $tablename, $set_values, $source_fields = null )
    {
        if( $this->connect() === false )
        {
            return false;
        }

        $query_where = $this->generate_fieldsvars_list( $source_fields, 'where', 'AND' );

        $query = ' UPDATE ' . $tablename
               .    ' SET ' . $this->generate_fieldsvars_list( $set_values, 'set', ',' ) . ' '
               . ( $query_where ? ( ' WHERE ' . $query_where ) : '' )
               ;

        $qresult = new SPIstore__pgsql__query( $this->db_handler, $query );

        if( $qresult->is_failed() )
        {
            $this->last_error_query   = $query;
            $this->last_error_message = $qresult->get_last_error_message();

            trigger_error( "Query failed on update [{$this->last_error_message}] [{$this->last_error_query}]", E_USER_WARNING );

            unset( $qresult );
            return false;
        }

        $qresult->close();
        unset( $qresult );
        return true;
    }

    public function delete( $tablename, $source_fields = null )
    {
        $query_where = $this->generate_fieldsvars_list( $source_fields, 'where', 'AND' );

        $query = ' DELETE FROM ' . $tablename
               . ( $query_where ? ' WHERE '.$query_where : '' )
               ;

        $qresult = new SPIstore__pgsql__query( $this->db_handler, $query );

        if( $qresult->is_failed() )
        {
            $this->last_error_query   = $query;
            $this->last_error_message = $qresult->get_last_error_message();

            trigger_error( "Query failed on delete [{$this->last_error_message}] [{$this->last_error_query}]", E_USER_WARNING );

            unset( $qresult );
            return false;
        }

        $qresult->close();
        unset( $qresult );
        return true;
    }

    public function begin()
    {
        $this->query( 'BEGIN;', null, true );
    }

    public function commit()
    {
        $this->query( 'COMMIT;', null, true );
    }

    public function rollback()
    {
        $this->query( 'ROLLBACK;', null, true );
    }

    public function get_last_error_query()
    {
        return $this->last_error_query;
    }

    public function get_last_error_message()
    {
        return $this->last_error_message;
    }

    public function prepare_search_query( $fieldname, $string )
    {
        $string = preg_replace( '/( +)/u', ' ', trim( $string ) );
        
        if( $string == '' )
        {
            return false;
        }
        
        list( $fname1, $fname2 ) = explode( '.', $fieldname, 2 );
        
        if( $fname1 && $fname2 )
        {
            $fieldname = '"'.$fname1.'"."'.$fname2.'"';
        }
        else
        {
            $fieldname = '"'.$fname1.'"';
        }
        
        $result = '';
        
        $words = explode( ' ', $string );
        
        foreach( $words as $word )
        {
            $result .= ( $result ? ' AND ' : '' ) . ' (' . $fieldname . ')::text ILIKE \'%' . addslashes( $word ) . '%\' ';
        }

        return ' ( '.$result.' ) ';
    }

    private function generate_fieldsvars_list( $fields, $mode, $joiner )
    {
        if( is_array( $fields ) === false )
        {
            return '';
        }
        
        $result = '';

        foreach( $fields as $key => $value )
        {
            $result .= ( $result === '' ? '' : ' '.$joiner.' ' );
            
            switch( $mode ) 
            {
                case 'set':
                {
                    if( $value === null )
                    {
                        $result .= ' "'.$key.'"=NULL ';
                    }
                    else
                    {
                        $result .= ' "'.$key.'"=\''.addslashes( (string) $value ).'\' ';
                    }
                    break;
                }
                
                case 'where':
                {
                    $key_parts = explode( ':', $key, 2 );
                    
                    if( count( $key_parts ) == 1 )
                    {
                        $key_parts[0] = $key;
                        $key_parts[1] = '=';
                    }
                    if( count( $key_parts ) >= 3 )
                    {
                        # ???
                    }
        
                    $key = $key_parts[0];
                    
                    switch( $key_parts[1] )
                    {
                        case '=':
                        {
                            $operator = '=';
                            $operator_null = ' IS ';
                            break;
                        }

                        case '>':
                        case '>=':
                        case '<':
                        case '<=':
                        {
                            $operator = $key_parts[1];
                            $operator_null = ' ? ';
                            break;
                        }

                        case '<>':
                        case '!':
                        case 'not':
                        {
                            $operator = ' <> ';
                            $operator_null = ' IS NOT ';
                            break;
                        }
                        
                        default:
                        {
                            $operator = ' ?undefined_operator? ';
                            $operator_null = ' ?undefined_operator_null? ';
                            return false;
                        }
                    }
                    unset( $key_parts );

                    if( $value === null )
                    {
                        $result .= ' "'.$key.'"'.$operator_null.' NULL ';
                    }
                    elseif( is_numeric( $value ) === true )
                    {
                        $result .= ' "'.$key.'"'.$operator.$value;
                    }
                    else
                    {
                        $result .= ' "'.$key.'"'.$operator."'".addslashes( (string) $value )."'";
                    }
                    break;
                }
            }
        }

        return $result;
    }

    public function get_stat()
    {
        return $this->stat;
    }
}
